﻿using System.Collections;
using System.Dynamic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;

namespace SyncFileCommon.Class
{
    public class JsonHelper
    {

        public JsonHelper()
        {

        }

        public JsonHelper(string js)
        {
            JsonStr = js;
        }

        /// <summary>
        /// 变量JSON字符串
        /// </summary>
        public string JsonStr
        {
            get { return GetJsonStr(); }
            set { SetJsonStr(value); }
        }

        Hashtable ht = new Hashtable();

        /// <summary>
        /// 获取Json字符串所有key
        /// </summary>
        /// <param name="VarName">变量名</param>
        /// <returns></returns>
        public List<string> GetAllKey()
        {
            if (JsonStr.ToLower() == "null")
            {
                return null;
            }
            if (JsonStr == "")
            {
                return null;
            }
            List<string> _keyList = new List<string>();
            foreach (string key in ht.Keys)
            {
                _keyList.Add(key);
            } 
            return _keyList;

        }

        /// <summary>
        /// 增加变量
        /// </summary>
        /// <param name="VarName">变量名</param>
        /// <param name="VarValue">变量值</param>
        public JsonHelper AddVar(string VarName, object VarValue)
        {

            if (ht.Contains(VarName))
            {
                ht[VarName] = VarValue;
            }
            else
            {
                ht.Add(VarName, VarValue);
            }
            return this;

        }

        /// <summary>
        /// 获取变量值
        /// </summary>
        /// <param name="VarName">变量名</param>
        /// <returns></returns>
        public string GetVar(string VarName)
        {
            if (JsonStr.ToLower() == "null")
            {
                return null;
            }
            if (JsonStr == "")
            {
                return "";
            }
            if (ht.Contains(VarName))
            {
                if (ht[VarName] != null)
                    return ht[VarName].ToString();
            }
            return null;

        }

        /// <summary>
        /// 清除所有变量
        /// </summary>
        public void Clear()
        {
            ht.Clear();
        }

        /// <summary>
        /// 移除变量
        /// </summary>
        /// <param name="VarName"></param>
        public void RemoveVar(string VarName)
        {
            ht.Remove(VarName);
        }

        /// <summary>
        /// 通过变量表获取JSON字符串
        /// </summary> 
        /// <returns></returns>
        private string GetJsonStr()
        {
            return GetJsonStrFromObject(ht, false);
        }

        /// <summary>
        /// 将JSON字符串转换成变量表
        /// </summary>
        /// <param name="js"></param>
        private void SetJsonStr(string js)
        {
            ht = GetObjectFromJsonStr<Hashtable>(js);
        }



        /// <summary>
        /// 获取对象JSON字符串
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static string GetJsonStrFromObject(object o, bool indented = false)
        {
            JsonSerializerOptions options = new JsonSerializerOptions
            {
                MaxDepth = 64,//json循环最大深度 如果你所需的资源确实超过了32层，可以加深深度来解决 
                PropertyNamingPolicy = null,//对象属性名不改变大小写
                Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),//取消Unicode编码
                ReadCommentHandling = JsonCommentHandling.Skip,//允许在 JSON 输入中使用注释并忽略它们 
                AllowTrailingCommas = true,//允许额外符号
                PropertyNameCaseInsensitive = false,//反序列化过程中属性名称是否使用不区分大小写的比较
                NumberHandling = JsonNumberHandling.AllowReadingFromString,// 允许在反序列化的时候原本应为数字的字符串（带引号的数字）转为数字
                WriteIndented = indented,// 整齐输出

            };
            options.Converters.Add(new DateTimeConverterUsingDateTimeParse());
            options.Converters.Add(new LongJsonConverter());

            return JsonSerializer.Serialize(o, options);
        }

        /// <summary>
        /// 根据JSON字符串获取对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="s"></param>
        /// <returns></returns>
        public static T GetObjectFromJsonStr<T>(string s)
        {
            if (string.IsNullOrWhiteSpace(s) || s.ToLower() == "null")
            {
                object _o = null;
                return (T)_o;
            }

            JsonSerializerOptions options = new JsonSerializerOptions
            {
                MaxDepth = 64,//json循环最大深度 如果你所需的资源确实超过了32层，可以加深深度来解决 
                PropertyNamingPolicy = null,//对象属性名不改变大小写
                Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),//取消Unicode编码
                ReadCommentHandling = JsonCommentHandling.Skip,//允许在 JSON 输入中使用注释并忽略它们 
                AllowTrailingCommas = true,//允许属性值末尾存在逗号
                PropertyNameCaseInsensitive = false,//反序列化过程中属性名称是否使用不区分大小写的比较
                NumberHandling = JsonNumberHandling.AllowReadingFromString,// 允许在反序列化的时候原本应为数字的字符串（带引号的数字）转为数字
                WriteIndented = true,// 整齐输出
                IgnoreReadOnlyProperties = true,// 忽略只读字段 
            };
            options.Converters.Add(new DateTimeConverterUsingDateTimeParse());
            options.Converters.Add(new LongJsonConverter());

            return JsonSerializer.Deserialize<T>(s, options);
        }

        /// <summary>
        /// 转换时间时控制字符串的格式
        /// </summary>
        private class DateTimeConverterUsingDateTimeParse : JsonConverter<DateTime>
        {
            public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
            {
                if (reader.TokenType == JsonTokenType.String)
                {
                    if (DateTime.TryParse(reader.GetString(), out DateTime date))
                        return date;
                }
                return reader.GetDateTime();
            }
            public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
            {
                writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
            }
        }

        /// <summary>
        /// 转换时将Long类型转换为字符串传给前端，解决前端Js无法处理Long类型的问题
        /// </summary>
        private class LongJsonConverter : JsonConverter<long?>
        {
            public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
            {
                if (reader.TokenType == JsonTokenType.String)
                {
                    if (Int64.TryParse(reader.GetString(), out Int64 date))
                        return date;
                }
                return reader.GetInt64();
            }

            public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options)
            {
                writer.WriteStringValue(value + "");
            }
        }

        /// <summary>
        /// 解决动态类型转换问题
        /// </summary>
        public class DynamicJsonConverter : JsonConverter<dynamic>
        {
            public override dynamic Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
            {

                if (reader.TokenType == JsonTokenType.True)
                {
                    return true;
                }

                if (reader.TokenType == JsonTokenType.False)
                {
                    return false;
                }

                if (reader.TokenType == JsonTokenType.Number)
                {
                    if (reader.TryGetInt64(out long l))
                    {
                        return l;
                    }

                    return reader.GetDouble();
                }

                if (reader.TokenType == JsonTokenType.String)
                {
                    if (reader.TryGetDateTime(out DateTime datetime))
                    {
                        return datetime;
                    }

                    return reader.GetString();
                }

                if (reader.TokenType == JsonTokenType.StartObject)
                {
                    using JsonDocument documentV = JsonDocument.ParseValue(ref reader);
                    return ReadObject(documentV.RootElement);
                }
                // Use JsonElement as fallback.
                // Newtonsoft uses JArray or JObject.
                JsonDocument document = JsonDocument.ParseValue(ref reader);
                return document.RootElement.Clone();
            }

            private object ReadObject(JsonElement jsonElement)
            {
                IDictionary<string, object> expandoObject = new ExpandoObject();
                foreach (var obj in jsonElement.EnumerateObject())
                {
                    var k = obj.Name;
                    var value = ReadValue(obj.Value);
                    expandoObject[k] = value;
                }
                return expandoObject;
            }

            private object? ReadValue(JsonElement jsonElement)
            {
                object? result = null;
                switch (jsonElement.ValueKind)
                {
                    case JsonValueKind.Object:
                        result = ReadObject(jsonElement);
                        break;
                    case JsonValueKind.Array:
                        result = ReadList(jsonElement);
                        break;
                    case JsonValueKind.String:
                        //TODO: Missing Datetime&Bytes Convert
                        result = jsonElement.GetString();
                        break;
                    case JsonValueKind.Number:
                        //TODO: more num type
                        result = 0;
                        if (jsonElement.TryGetInt64(out long l))
                        {
                            result = l;
                        }
                        break;
                    case JsonValueKind.True:
                        result = true;
                        break;
                    case JsonValueKind.False:
                        result = false;
                        break;
                    case JsonValueKind.Undefined:
                    case JsonValueKind.Null:
                        result = null;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
                return result;
            }

            private object? ReadList(JsonElement jsonElement)
            {
                IList<object?> list = new List<object?>();
                foreach (var item in jsonElement.EnumerateArray())
                {
                    list.Add(ReadValue(item));
                }
                return list.Count == 0 ? null : list;
            }

            public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
            {
                writer.WriteStringValue(value.ToString());
            }
        }

    }
}
